package cn.maihe.elg.operation.supports.electronicseal.anne;

import cn.hutool.core.util.IdUtil;
import cn.hutool.http.HttpUtil;
import cn.maihe.elg.operation.config.AnneSignConfig;
import cn.maihe.elg.operation.config.ElgConfig;
import cn.maihe.elg.operation.exception.ErrorCode;
import cn.maihe.elg.operation.exception.SystemRuntimeException;
import cn.maihe.elg.operation.supports.electronicseal.anne.bean.annesign.base.SecureApiRequest;
import cn.maihe.elg.operation.supports.electronicseal.anne.bean.annesign.base.SecureApiResponse;
import cn.maihe.elg.operation.supports.electronicseal.anne.bean.annesign.login.LoginRequest;
import cn.maihe.elg.operation.supports.electronicseal.anne.bean.annesign.login.LoginResponse;
import cn.maihe.elg.operation.supports.electronicseal.anne.bean.annesign.login.LoginResponseBody;
import cn.maihe.elg.operation.supports.electronicseal.anne.utils.annesign.AnneSignConst;
import cn.maihe.elg.operation.supports.electronicseal.anne.utils.annesign.ApiException;
import cn.maihe.elg.operation.supports.electronicseal.anne.utils.annesign.JsonUtils;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import javax.annotation.PostConstruct;
import java.lang.reflect.ParameterizedType;
import java.text.MessageFormat;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

/**
 * @Description
 * @Author WJH
 * @Date 2021/08/13
 */
@Service
@Slf4j
public abstract class BaseAnneSignService<REQ extends SecureApiRequest, RESP extends SecureApiResponse> {
    private AnneSignConfig anneSignConfig;
    private ElgConfig elgConfig;
    protected  AnneSignConfig.TemplateContractInfo applyTemplateInfo;
    protected AnneSignConfig.TemplateContractInfo guaranteeProtocol;
    protected AnneSignConfig.TemplateContractInfo receiveProtocol;

    private final static Cache<String, LoginResponseBody> accountLoginCache = CacheBuilder.newBuilder().expireAfterWrite(60, TimeUnit.MINUTES).build();
    private final static String ACCOUNT_LOGIN_KEY = "ACCOUNT_LOGIN_KEY";
    protected final String ANNE_NOTIFY_URL;

    protected Class<REQ> reqClass;
    protected Class<RESP> respClass;

    public BaseAnneSignService(AnneSignConfig anneSignConfig, ElgConfig elgConfig) {

        ParameterizedType pType = (ParameterizedType) this.getClass().getGenericSuperclass();
        this.reqClass = (Class<REQ>) pType.getActualTypeArguments()[0];
        this.respClass = (Class<RESP>) pType.getActualTypeArguments()[1];

        this.elgConfig = elgConfig;
        this.anneSignConfig = anneSignConfig;

        this.ANNE_NOTIFY_URL = this.elgConfig.getApiBaseUrl() + AnneSignConst.CALLBACK_NOTIFY_URL;

      /*  Assert.notEmpty(anneSignConfig.getTemplateMap(), "参数[cn.maihe.elg.anne.template-map]安妮相关合同模板未配置");

        this.applyTemplateInfo = anneSignConfig.getTemplateMap().get("applyProtocol");

        this.guaranteeProtocol = anneSignConfig.getTemplateMap().get("guaranteeProtocol");
        Assert.notNull(this.guaranteeProtocol, "参数[cn.maihe.elg.anne.template-map.guaranteeProtocol]投标保证金担保保函合同模板未配置");
        this.receiveProtocol = anneSignConfig.getTemplateMap().get("receiveProtocol");
        Assert.notNull(this.receiveProtocol, "参数[cn.maihe.elg.anne.template-map.receiveProtocol]到账证明模板未配置");
*/
    }


    @PostConstruct
    public void init() {
//        doLogin();
    }

    private synchronized LoginResponseBody doLogin() {
        try {
            LoginResponseBody responseBody = accountLoginCache.getIfPresent(ACCOUNT_LOGIN_KEY);
            if (responseBody != null) {
                return responseBody;
            }

            LoginRequest request = new LoginRequest();
            request.setTimestamp(System.currentTimeMillis());
            request.setUuid(IdUtil.fastSimpleUUID());
            request.setVersion(AnneSignConst.API_VERSION_1);
            request.getBody().setLoginName(anneSignConfig.getAccountName());
            request.getBody().setPassword(anneSignConfig.getPassword());

            String apiUrl = this.anneSignConfig.getServerUrl() + AnneServiceName.login.getUrl();
            String requestJson = JsonUtils.toJson(request);

            log.info("uuid:{} >> {} - 请求报文:{}", request.getUuid(), AnneServiceName.login.getName(), requestJson);
            String responseJson = HttpUtil.post(apiUrl, requestJson);
            log.info("uuid:{} << {} - 响应报文:{}", request.getUuid(), AnneServiceName.login.getName(), responseJson);
            Assert.hasText(responseJson, "响应报文为空");

            LoginResponse response = JsonUtils.fromJson(responseJson, LoginResponse.class);
            Assert.notNull(response, "响应报文体为空");

            if (!response.isOk()) {
                log.error("安妮电签登录失败:[{}:{}]", response.getCode(), response.getDetail());
                throw new RuntimeException(MessageFormat.format("登录失败[{}:{}]", response.getCode(), response.getDetail()));
            }
            Assert.notNull(response.getBody(), "响应报文体body为空");

            accountLoginCache.put(ACCOUNT_LOGIN_KEY, response.getBody());
            log.info("安妮账户登录成功;loginName:{}, accountId: {}", anneSignConfig.getAccountName(), response.getBody().getAccountId());
            return response.getBody();
        } catch (RuntimeException e) {
            log.error("安妮电签账户登录失败:" + e.getMessage(), e);
            throw new SystemRuntimeException(ErrorCode.ERROR, "账户登录失败:" + e.getMessage());
        }

    }

    protected LoginResponseBody getLoginResponseBody() {
        try {
            return accountLoginCache.get(ACCOUNT_LOGIN_KEY, this::doLogin);
        } catch (ExecutionException e) {
            throw new SystemRuntimeException(ErrorCode.ERROR, "获取登录信息异常：" + e.getMessage());
        }
    }

    protected String getSecretKey() {
        return this.getLoginResponseBody().getSecretKey();
    }

    /**
     * 接口方法枚举
     */
    protected abstract AnneServiceName serviceName();

    /**
     * 构造请求参数
     */
    protected REQ buildBaseReq() {
        REQ req;
        try {
            req = this.reqClass.newInstance();
            req.setTimestamp(System.currentTimeMillis());
            req.setUuid(IdUtil.fastSimpleUUID());
            req.setAccountId(this.getLoginResponseBody().getAccountId());
            req.setSign(req.getSign(this.getSecretKey()));
        } catch (InstantiationException | IllegalAccessException e) {
            throw new RuntimeException("实例化" + reqClass.getName() + "异常:" + e.getMessage());
        }
        return req;
    }

    /**
     * 子类调用
     * 保函 状态判断及验签操作
     * 非成功状态码抛异常
     */
    protected RESP doPost(REQ request) {
        Assert.hasText(request.getSign(), "请求参数未签名");
        String apiUrl = this.getReqApiUrl();
        String requestJson = JsonUtils.toJson(request);

        log.info("uuid:{} >> {} - 请求报文:{}", request.getUuid(), serviceName().getName(), requestJson);
        String responseJson = null;
        try {
            responseJson = HttpUtil.post(apiUrl, requestJson);
        } catch (Exception e) {
            log.error("uuid:{} << {} - 服务异常:{}", request.getUuid(), serviceName().getName(), e.getMessage(), e);
            throw new ApiException("服务异常:" + e.getMessage(), e);
        }
        log.info("uuid:{} << {} - 响应报文:{}", request.getUuid(), serviceName().getName(), responseJson);
        Assert.hasText(responseJson, "响应报文为空");

        RESP response = JsonUtils.fromJson(responseJson, this.respClass);
        Assert.notNull(response, "响应报文体为空");

        //企业创建结果查询 1020 1404 标识企业不存在，则可用创建
        //响应码非0情况下 response.getSign()为空即上游没有加签操作
        if (serviceName() == AnneServiceName.enterprise_query) {
            if (isNotExistsEnterprise(response)) {
                return response;
            } else if (!response.isOk(getSecretKey())) {
                throw new ApiException("响应码非成功状态" + response.respMsg());
            }
        } else if (!response.isOk(this.getSecretKey())) {
            throw new ApiException(String.format("响应失败[%s:%s]", response.getCode(), response.getDetail()));
        }

        return response;
    }

    /**
     * 不存在企业的状态码
     */
    protected boolean isNotExistsEnterprise(RESP response) {
        return response.getCode() == 1020 || response.getCode() == 1404;
    }

    /**
     * this.anneSignConfig.getServerUrl() 不包含 "/"结尾
     * relativeUrl 一定以"/"开头
     *
     * @return
     */
    private String getReqApiUrl() {
        return this.anneSignConfig.getServerUrl() + this.serviceName().getUrl();
    }


}
